<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>书本翻页效果</title>
    <style>
      /*css样式*/
      :root {
        --width: 59.2rem;
        --height: 40rem;
        --font-size: 1.2rem;
        --animation-duration: 2s;
      }

      body {
        background-color: #4f4e68;
        height: 100%;
        overflow: hidden;
      }

      .book {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        margin: auto;
        width: var(--width);
        height: var(--height);
        perspective: 70rem;
      }

      .coverEnd {
        right: -0.5rem !important;
      }

      .cover {
        background-color: #36354e;
        transform: rotateY(0deg);
        width: calc(var(--width) / 2);
        height: var(--height);
        overflow: hidden;
      }

      .cover-image {
        width: 100%;
        height: 100%;
        object-fit: cover;
        pointer-events: none;
        position: absolute;
        top: 0;
        left: 0;
        z-index: 0;
        opacity: 0.8;
      }

      .cover-title {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        pointer-events: none;
        background: linear-gradient(45deg, #ff0000, #ff7f00, #ffff00);
        background-clip: text;
        -webkit-background-clip: text;
        color: transparent;
        font-size: 2rem;
        font-weight: bold;
        text-align: center;
        z-index: 1;
        width: 100%;
      }

      .page {
        width: calc(var(--width) / 2 - 0.5rem);
        height: calc(var(--height) - 0.5rem);
        background-color: #e9e6c4;
        transform: rotateY(0deg);
        text-align: right;
        font-size: var(--font-size);
        color: #777;
        font-family: monospace;
        overflow: hidden;
        text-indent: 2em;
      }

      .page::after {
        display: block;
        content: "";
        position: absolute;
        top: calc(var(--font-size) * 2);
        left: 1rem;
        right: 1rem;
        bottom: 1rem;
        background-image: linear-gradient(
          to bottom,
          currentColor 1px,
          transparent 1px
        );
        background-size: 100% calc(var(--font-size) + 0.5rem); /* 调整行高 */
        opacity: 1; /* 调整透明度 */
        pointer-events: none; /* 让点击事件穿透伪元素 */
      }

      .cover,
      .page {
        position: absolute;
        padding-top: 1rem;
        transform-origin: 0 0;
        border-radius: 5px 0 0 5px;
        box-shadow: inset 3px 0px 20px rgba(0, 0, 0, 0.2),
          0px 0px 15px rgba(0, 0, 0, 0.1);
        box-sizing: border-box;
        text-align: left;
        right: 0;
      }

      .page-title {
        transform: translate(0, 0);
        pointer-events: none;
        background: linear-gradient(45deg, #ff0000, #ff7f00, #ffff00);
        background-clip: text;
        -webkit-background-clip: text;
        color: transparent;
        font-weight: bold;
        text-align: center;
        text-indent: 0;
      }

      .page-text {
        mix-blend-mode: hard-light;
        color: #000000;
        padding-left: 1em;
        padding-right: 1em;
        font-size: calc(var(--font-size) * 0.9);
        line-height: calc(var(--font-size) + 0.5rem);
        font-weight: bold;
      }

      .page-image {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        object-fit: cover;
        pointer-events: none;
        opacity: 0.8;
        z-index: 0;
      }

      .turn {
        animation: turnForward var(--animation-duration) forwards;
      }

      .turnBack {
        animation: turnBackward var(--animation-duration) forwards;
      }

      .front-page,
      .back-page {
        overflow: hidden;
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        pointer-events: none;
        right: 0;
        bottom: 0;
        margin: 0;
        padding-top: 1rem;
        z-index: 1;
      }

      .back-page {
        transform: rotateY(180deg);
        visibility: hidden;
      }

      .turn-page.turn > div.front-page {
        animation: fadeOut var(--animation-duration) forwards;
      }

      .turn-page.turnBack > div.front-page {
        animation: fadeIn var(--animation-duration) forwards;
      }

      .turn-page.turnBack > div.back-page {
        animation: fadeOut var(--animation-duration) forwards;
      }

      .turn-page.turn > div.back-page {
        animation: fadeIn var(--animation-duration) forwards;
      }

      /* 淡出动画 */
      @keyframes fadeOut {
        0% {
          visibility: visible;
        }
        49% {
          visibility: visible;
        }
        50% {
          visibility: hidden;
        }
        100% {
          visibility: hidden;
        }
      }

      /* 淡入动画 */
      @keyframes fadeIn {
        0% {
          visibility: hidden;
        }
        50% {
          visibility: hidden;
        }
        51% {
          visibility: visible;
        }
        100% {
          visibility: visible;
        }
      }

      @keyframes turnForward {
        0% {
          z-index: 999;
        }
        40% {
          z-index: 999;
        }
        49% {
          transform: rotateY(-90deg);
        }
        50% {
          transform: rotateY(-90deg);
        }
        100% {
          transform: rotateY(-180deg);
          z-index: null;
        }
      }

      @keyframes turnBackward {
        0% {
          z-index: 999;
        }
        40% {
          z-index: 999;
        }
        49% {
          transform: rotateY(-90deg);
        }
        50% {
          transform: rotateY(-90deg);
        }
        100% {
          transform: rotateY(0deg);
          z-index: null;
        }
      }
    </style>
  </head>
  <body>
    <div class="book"></div>
    <script>
      //js逻辑
      const book = document.querySelector(".book");
      let isTurnIng = false;
      let showPageNumber = 0;
      let timer = null;
      const pages = [
        {
          front: {
            title: "《千与千寻》",
            text: "少女千寻误入神灵世界，为救变成猪的父母经历磨难，最终以勇气与善意赢得自由。影片隐喻现代社会异化与人性救赎，获奥斯卡最佳动画长片+柏林金熊奖，全球票房超3.5亿美元，成为日本影史巅峰。",
            background: "../images/宫崎骏/千与千寻.jpg",
          },
          back: {
            title: "《天空之城》",
            text: "吉卜力工作室开山之作。少年帕祖与持有飞行石的少女希达为阻止军方夺取古代浮空文明“拉普达”的毁灭性武器，展开空中冒险。影片探讨科技异化与人性贪婪，久石让的配乐成为经典，结尾的“生命之树”象征自然永恒。",
            background: "../images/宫崎骏/天空之城.jpg",
          },
        },
        {
          front: {
            title: "《悬崖上的金鱼姬》",
            text: "人鱼公主波妞逃离海洋，与人类男孩宗介缔结纯真友谊，挑战自然与人类的隔阂。手绘水彩风格营造童话质感，灵感源自《小美人鱼》，强调孩童视角的信任与生态和谐。",
            background: "../images/宫崎骏/悬崖上的金鱼姬.jpg",
          },
          back: {
            title: "《魔女宅急便》",
            text: "13岁魔女琪琪带黑猫吉吉离家修行，在沿海城市以飞行技能送货谋生。经历成长挫折后重拾自信，学会独立与责任。欧洲风情背景与少女心路历程相融，诠释青春期的自我认同与社会融入。",
            background: "../images/宫崎骏/魔女宅急便.jpg",
          },
        },
        {
          front: {
            title: "《鲁邦三世：卡里奥斯特罗城》",
            text: "宫崎骏首部独立执导的剧场动画。传奇大盗鲁邦三世在虚构欧洲公国“卡里奥斯特罗”邂逅被追捕的少女克拉丽丝，联手对抗阴谋集团。影片融合冒险、浪漫与幽默，奠定宫崎骏对飞行器与欧洲风情的偏爱，被誉为“史上最佳动画电影之一”。",
            background: "../images/宫崎骏/鲁邦三世.jpg",
          },
          back: {
            title: "《龙猫》",
            text: "宫崎骏执导的经典动画，讲述龙猫与他的伙伴们的故事。影片以细腻的画面和感人的故事，展现了龙猫的成长和成长过程，强调了爱与勇气的力量。",
            background: "../images/宫崎骏/龙猫.jpg",
          },
        },
        {
          front: {
            title: "《风之谷》",
            text: "改编自宫崎骏同名漫画。千年后的人类文明因污染濒临崩溃，风之谷公主娜乌西卡以智慧化解人类与巨型虫族的战争，揭示自然与生命的共生真谛。电影开创“环保人文”主题，震撼的末日世界观与女主角的勇敢形象影响深远。",
            background: "../images/宫崎骏/风之谷.jpg",
          },
          back: {
            title: "《哈尔的移动城堡》",
            text: "改编自英国小说。少女苏菲被巫婆诅咒衰老，进入魔法师哈尔的移动城堡，共同对抗战争并解开诅咒。探讨爱与自我接纳，蒸汽朋克美学与反战主题交织，展现“衰老亦是成长”的哲思。",
            background: "../images/宫崎骏/哈尔的移动城堡.jpg",
          },
        },
        {
          front: {
            title: "《幽灵公主》",
            text: "室町时代少年阿席达卡为破除诅咒西行，卷入人类开发势力与森林神兽的战争，并与狼女珊共同寻求生存之道。史诗级生态寓言，批判文明扩张的代价，制作耗资20亿日元，创日本票房纪录。",
            background: "../images/宫崎骏/幽灵公主.jpg",
          },
          back: {
            title: "《起风了》",
            text: "以零式战机设计师堀越二郎为原型，讲述其追逐航空梦想却深陷战争矛盾的一生。现实题材罕见，融合历史反思与个人理想主义，获日本电影学院奖最佳动画，宫崎骏宣布引退（后复出）。",
            background: "../images/宫崎骏/起风了.jpg",
          },
        },
        {
          front: {
            title: "《借东西的小人阿莉埃蒂》",
            text: "少女阿莉埃蒂是生活在人类家中的小小 “借东西族”。她与人类少年翔相遇后，家族因被人类发现而被迫搬家。该片由宫崎骏企划，米林宏昌执导，以细腻笔触描绘人与自然的微妙关系。",
            background: "../images/宫崎骏/借东西的小人阿莉埃蒂.jpg",
          },
          back: {
            title: "《侧耳倾听》",
            text: "宫崎骏编剧，近藤喜文导演。初中生月岛雯与天泽圣司因书籍结缘，共同追求梦想的青春物语。细腻描绘少年成长中的自我觉醒与朦胧情感。",
            background: "../images/宫崎骏/侧耳倾听.jpg",
          },
        },
        {
          front: {
            title: "《红猪》",
            text: "一战后的亚得里亚海上，被诅咒变成猪的王牌飞行员波鲁克化身赏金猎人，打击空贼并守护心爱之人。影片以浪漫喜剧包装中年危机反思，蕴含反战思想与自由精神，被称为宫崎骏的“自画像”。",
            background: "../images/宫崎骏/红猪.jpg",
          },
          back: {
            title: "《你想活出怎样的人生》",
            text: "少年真人在母亲离世后进入神秘塔楼，跨越生死界探索生命意义。隐喻宫崎骏人生回顾，手绘与CG结合，获奥斯卡最佳动画长片，被称“集大成之作”。",
            background: "../images/宫崎骏/你想活出怎样的人生.jpg",
          },
        },
      ];

      const covers = [
        {
          front: {
            title: "『宫崎骏作品集』",
            background: "../images/宫崎骏/合集封面.jpg",
          },
          back: {},
        },
        {
          front: {},
          back: {
            title: "",
            background: "../images/宫崎骏/合集.jpg",
          },
        },
      ];

      function createPage(info = {}, isCover = false) {
        const pageElement = document.createElement("div");
        const pre = isCover ? "cover" : "page";
        if (info.background) {
          const img = document.createElement("img");
          img.classList.add(`${pre}-image`);
          img.src = info.background;
          pageElement.appendChild(img);
        }
        if (info.title) {
          const titleElement = document.createElement("div");
          titleElement.innerText = info.title;
          titleElement.classList.add(`${pre}-title`);
          pageElement.appendChild(titleElement);
        }
        const textElement = document.createElement("div");
        textElement.innerText = info.text || "";
        textElement.classList.add(`${pre}-text`);
        pageElement.appendChild(textElement);
        return pageElement;
      }

      function createFrontAndBackPage(pageElement, page = {}, isCover = false) {
        if (!isCover) pageElement.classList.add("page");
        pageElement.classList.add("turn-page");
        const frontPage = createPage(page.front, isCover);
        frontPage.classList.add("front-page");
        const backPage = createPage(page.back, isCover);
        backPage.classList.add("back-page");
        pageElement.appendChild(frontPage);
        pageElement.appendChild(backPage);
      }

      function createPages(page) {
        pages.reverse().forEach((page, index) => {
          const pageElement = document.createElement("span");
          createFrontAndBackPage(pageElement, page);
          pageElement.setAttribute("data-index", pages.length - index);
          book.appendChild(pageElement);
        });
      }

      const coverEnd = document.createElement("span");
      coverEnd.classList.add("cover");
      coverEnd.classList.add("coverEnd");
      coverEnd.setAttribute("data-index", pages.length + 1);
      createFrontAndBackPage(coverEnd, covers[1], true);
      book.appendChild(coverEnd);
      createPages();
      const cover = document.createElement("span");
      cover.classList.add("cover");
      cover.setAttribute("data-index", "0");
      createFrontAndBackPage(cover, covers[0], true);
      book.appendChild(cover);
      const root = document.documentElement;
      const computedStyle = getComputedStyle(root);
      const duration = computedStyle.getPropertyValue("--animation-duration");

      function turnPageFn() {
        const coverElements = book.querySelectorAll(".cover");
        const pageElements = book.querySelectorAll(".page");
        pageElements.forEach((page, index) => {
          page.addEventListener("click", pageClick);
        });
        coverElements.forEach((cover, index) => {
          cover.addEventListener("click", pageClick);
        });
      }
      function calZIndex(selector = ".turn") {
        const turnedPages = document.querySelectorAll(selector);
        return turnedPages.length + 1;
      }
      function pageClick(e) {
        const target = e.target;
        const index = parseInt(target.getAttribute("data-index"));
        if (![showPageNumber, showPageNumber - 1].includes(index)) {
          return;
        }
        if (target.classList.contains("turn")) {
          if (isTurnIng === "turnBack") return;
          showPageNumber = index;
          isTurnIng = "turn";
          target.style.transform = "rotateY(-180deg)";
          target.classList.remove("turn");
          target.classList.add("turnBack");
          target.style.zIndex = null;
        } else {
          if (isTurnIng === "turn") return;
          showPageNumber = index + 1;
          isTurnIng = "turnBack";
          target.style.transform = "rotateY(0deg)";
          target.classList.remove("turnBack");
          target.classList.add("turn");
          target.style.zIndex = calZIndex();
        }
        clearTimeout(timer);
        timer = setTimeout(() => {
          isTurnIng = false;
        }, parseFloat(duration) * 1000);
      }
      turnPageFn();
    </script>
  </body>
</html>
